import pandas as pd
import numpy as np
import os
import datetime
import copy
import random
import Core.InstrumentType as InstrumentType
import Core.UniDataInterface as UniDataInterface
from Core.Exception import *
from Core.UniDataInterface import Load_Multi_DailyBar_DataFrame


# 生成随机Returns
def Generate_Random_Data(num_assets, num_obs, seed=1):
    # return_vec = np.random.randn(n_assets, n_obs)
    dates = pd.date_range('20130101', periods=num_obs)
    np.random.seed(seed)
    header = ["ABCDEFG"]
    data = pd.DataFrame(np.random.randn(num_obs, num_assets), index=dates)
    # print(data)

    returns = data / 100.0
    # returns = np.log(data / data.shift(1))
    # print(returns)
    #
    return returns


# 生成随机序列，计算收益，相关性，方差
def Generate_Random_Data_Return_Volatility_Correlation(num_assets, num_obs, seed=1, annualized_days=245):
    #
    df_returns = Generate_Random_Data(num_assets, num_obs, seed)
    #
    mean = df_returns.mean() * annualized_days
    expected_returns = mean.tolist()
    #
    sd = df_returns.std() * np.sqrt(annualized_days)
    expected_volatilities = sd.tolist()
    #
    correlation_matrix = df_returns.corr().values
    #
    covariance_matrix = Calc_Covariance_Matrix(correlation_matrix, expected_volatilities)
    #
    return {"Returns": expected_returns, "Volatilities": expected_volatilities,
            "Correlation": correlation_matrix, "Covariance": covariance_matrix}


def Load_Daily_Returns_DataFrame(database, symbol_list, datetime1, datetime2, is_log, instrument_type_list=[]):
    # 读取数据
    df = Load_Multi_DailyBar_DataFrame(database, symbol_list,
                                       datetime1, datetime2,
                                       instrument_type_list=instrument_type_list)
    # 计算日收益
    if is_log:
        df = np.log(df / df.shift(1))  # 转换成对数收益率
    else:
        df = df / df.shift(1) - 1
    #
    return df


# 读取收益，
def Load_Daily_Returns_DataFrame_Calc_Return_Volatility_Correlation(database,
                                                                    symbol_list,
                                                                    datetime1, datetime2, is_log, annualized_days,
                                                                    instrument_type_list=[]):
    #
    df_returns = Load_Daily_Returns_DataFrame(database, symbol_list,
                                              datetime1, datetime2, is_log,
                                              instrument_type_list=instrument_type_list)
    #
    mean = df_returns.mean() * annualized_days
    expected_returns = mean.tolist()
    #
    sd = df_returns.std() * np.sqrt(annualized_days)
    expected_volatilities = sd.tolist()
    #
    correlation_matrix = df_returns.corr().values

    # 处理波动率为0的问题
    for i in range(len(expected_volatilities)):
        if expected_volatilities[i] == 0:
            expected_volatilities[i] = 0.0001

    # 无数据情况下无法计算相关性
    num_assets = len(expected_returns)
    for i in range(0, num_assets):
        for j in range(num_assets):
            # 空值处理
            if np.isnan(correlation_matrix[i][j]):
                if i == j:
                    correlation_matrix[i][j] = 1
                else:
                    correlation_matrix[i][j] = 0
            # 不允许出现 相关性为1的情况
            if i != j and correlation_matrix[i][j] == 1:
                correlation_matrix[i][j] = 0.9999
            # 不允许出现 相关性为0的情况
            if correlation_matrix[i][j] == 0:
                # ran = random.uniform(1, 100)
                # correlation_matrix[i][j] = 0.00001 * ran
                correlation_matrix[i][j] = 0.0001

    #
    covariance_matrix = Calc_Covariance_Matrix(correlation_matrix, expected_volatilities)
    #
    return {"Returns": expected_returns, "Volatilities": expected_volatilities,
            "Correlation": correlation_matrix, "Covariance": covariance_matrix}


# 各出时间序列，计算相关性，收益，波动率
def Calc_Return_Volatility_Correlation(df_returns, annualized_days):
    #
    mean = df_returns.mean() * annualized_days
    expected_returns = mean.tolist()
    #
    sd = df_returns.std() * np.sqrt(annualized_days)
    expected_volatilities = sd.tolist()
    #
    correlation_matrix = df_returns.corr().values
    #
    covariance_matrix = Calc_Covariance_Matrix(correlation_matrix, expected_volatilities)
    #
    return {"Returns": expected_returns, "Volatilities": expected_volatilities,
            "Correlation": correlation_matrix, "Covariance": covariance_matrix}


# 计算协方差矩阵
def Calc_Covariance_Matrix(correlation, annual_std_dev):
    #
    num_assets = len(annual_std_dev)
    covariance = np.zeros([num_assets, num_assets])
    #
    for i in range(0, num_assets):
        for j in range(num_assets):
            if j < i:
                correlation[i][j] = correlation[j][i]
            if j == i:
                correlation[i][j] = 1
    #
    # print("Correlation")
    # df = pd.DataFrame(correlation)
    # print(df)

    # CoVariance = Rho * Asset1 SD * Asset2 SD
    for i in range(num_assets):
        for j in range(num_assets):
            covariance[i][j] = correlation[i][j] * annual_std_dev[i] * annual_std_dev[j]
            # print(i, j, "Pho", correlation[i][j], "sd-i", annual_std_dev[i], "sd-j", annual_std_dev[j], "res", covariance[i][j])

    #
    # print("Covariance")
    return covariance


# 通过协方差矩阵反算波动率
def covariance_2_sd(covariance_matrix, correlation_matrx):

    sd_matrix = covariance_matrix / correlation_matrx

    sd_list = []
    num_assets = len(correlation_matrx)
    for i in range(num_assets):
        sd = np.sqrt(sd_matrix[i][i])
        sd_list.append(sd)
    #
    return sd_list

# 生成一个对角矩阵
def to_diagonal_matrix(value_list):
    num_assets = len(value_list)
    diagonal_matrix = np.identity(num_assets)
    for i in range(num_assets):
        diagonal_matrix[i][i] = value_list[i]
    #
    return diagonal_matrix


# 生成一个资产间相关性相同的矩阵
def Ave_Correlation_to_Matrix(average_rho, num_assets):
    correlation_matrix = np.identity(num_assets)
    for i in range(num_assets):
        for j in range(num_assets):
            if i == j:
                continue
            correlation_matrix[i][j] = average_rho
    #
    return correlation_matrix
